#pragma once

#include <vector>
#include <cassert>

// OCC 库
#include <TColgp_HArray1OfPnt.hxx>
#include <TColgp_SequenceOfPnt.hxx>
#include <Geom_BSplineSurface.hxx>
#include <GeomAPI_ProjectPointOnSurf.hxx>

// Eigen 库
#include <Dense>
#include <Sparse>
#include <Eigenvalues>

#include "Timing.h"

namespace Para
{
	class ParaBuilder
	{
	public:
		// 注意 TColgp_SequenceOfPnt 没有继承自 Standard_Transient，因此不能用 Handle
		ParaBuilder(Handle(TColgp_HArray1OfPnt) cloud);
		ParaBuilder(const TColgp_SequenceOfPnt& cloud);

		Handle(Geom_BSplineSurface) Build(
			std::vector<double>& u,				// u 参数向量
			std::vector<double>& v,				// v 参数向量
			int Nu, int Nv,						// 控制网格规模
			int p, int q,						// 次数
			std::vector<double>& w,				// 每个点的拟合权重
			double alpha = 1e-6,				// 弦长最小化权重
			double beta = 1e-3,					// 二阶平滑化权重
			int times = 3						// 优化次数
		);

	protected:
		Handle(TColgp_HArray1OfPnt) m_cloud;

		// 获得点云的有向包围盒
		std::vector<gp_Vec> OBB(Handle(TColgp_HArray1OfPnt) cloud);

		// 将输入参数规范化到 [0,1] 上
		void Normalize(std::vector<double>& u);

		// 找到 u 所在的节点区间 [u_i, u_{i+1})
		int FindSpan(
			double u,
			int p,
			const std::vector<double>& knots
		);

		// 计算所有代入 u 后不为零的 p 次基函数
		std::vector<double> BasisFuncs(
			int i,
			double u,
			int p,
			const std::vector<double>& knots
		);

		// 点正交投影算法
		void Projection(
			Handle(Geom_BSplineSurface) surf,
			gp_Pnt P,
			double& u, double& v,
			double tol
		);

		// OCC 正交投影
		void OCCProjection(
			Handle(Geom_BSplineSurface) surf,
			gp_Pnt P,
			double& u, double& v
		);

		// 弦长最小化拟合曲面
		Handle(Geom_BSplineSurface) Chord(
			Handle(TColgp_HArray1OfPnt) cloud,
			std::vector<double>& u,
			std::vector<double>& v,
			int Nu, int Nv,
			int p, int q,
			std::vector<double>& w,
			double alpha, double beta
		);
	};
};

